﻿using System.Windows.Input;
using MugenMvvmToolkit.Annotations;
using MugenMvvmToolkit.Attributes;
using MugenMvvmToolkit.Interfaces;
using MugenMvvmToolkit.Interfaces.Models;
using MugenMvvmToolkit.Interfaces.ViewModels;
using MugenMvvmToolkit.Interfaces.Views;
using MugenMvvmToolkit.Models;
using MugenMvvmToolkit.Utils;
#if TASKSUPPORT
using System.Threading.Tasks;
#endif

namespace MugenMvvmToolkit.ViewModels
{
    /// <summary>
    ///     Represents the view-model which can be used as page.
    /// </summary>
    [BaseViewModel(Priority = 3)]
    public abstract class NavigableViewModel : ViewModelBase, INavigableViewModel, IHasView, IHasDisplayName
    {
        #region Fields

        private INavigationContext _navigationContext;
        private string _displayName;
        private IView _view;
        private ICommand _goBackCommand;

        #endregion

        #region Constructors

        /// <summary>
        ///     Initializes a new instance of the <see cref="NavigableViewModel" /> class.
        /// </summary>
        protected NavigableViewModel()
        {
            GoBackCommand = new RelayCommand(o => GoBack(), o => CanGoBack, this);
        }

        #endregion

        #region Commands

        /// <summary>
        ///     Gets the command that navigates to the most recent item in back navigation history.
        /// </summary>
        public ICommand GoBackCommand
        {
            get { return _goBackCommand; }
            protected set
            {
                if (Equals(value, _goBackCommand)) return;
                _goBackCommand = value;
                OnPropertyChanged();
            }
        }

        #endregion

        #region Properties

        /// <summary>
        ///     Indicates whether the page can navigate back.
        /// </summary>
        protected bool CanGoBack
        {
            get
            {
                if (_navigationContext == null)
                    return false;
                return _navigationContext.NavigationProvider.NavigationService.CanGoBack;
            }
        }

        /// <summary>
        ///     Indicates whether the page can navigate forward.
        /// </summary>
        protected bool CanGoForward
        {
            get
            {
                if (_navigationContext == null)
                    return false;
                return _navigationContext.NavigationProvider.NavigationService.CanGoForward;
            }
        }

        #endregion

        #region Implementation of INavigableViewModel

        /// <summary>
        ///     Gets the <see cref="IView" />.
        /// </summary>
        public virtual IView View
        {
            get { return _view; }
            set
            {
                if (Equals(value, _view)) return;
                IView oldView = _view;
                _view = value;
                OnViewChanged(oldView, value);
                OnPropertyChanged("View");
            }
        }

        /// <summary>
        ///     Gets or sets the display name for the current <see cref="IViewModel" />.
        /// </summary>
        public virtual string DisplayName
        {
            get { return _displayName; }
            set
            {
                if (Equals(value, _displayName)) return;
                _displayName = value;
                OnPropertyChanged("DisplayName");
            }
        }

        /// <summary>
        ///     Called when a page becomes the active page in a frame.
        /// </summary>
        /// <param name="context">
        ///     The specified <see cref="INavigationContext" />.
        /// </param>
        void INavigableViewModel.OnNavigatedTo(INavigationContext context)
        {
            EnsureIsNotDisposed();
            OnNavigatedTo(context);
            _navigationContext = context;
            //To invalidate command state.
            Publish(StateChangedMessage.Empty);
        }

        /// <summary>
        ///     Called when a page is no longer the active page in a frame.
        /// </summary>
        /// <param name="context">
        ///     The specified <see cref="INavigationContext" />.
        /// </param>
        void INavigableViewModel.OnNavigatedFrom(INavigationContext context)
        {
            OnNavigatedFrom(context);
            _navigationContext = null;
        }

#if TASKSUPPORT
        /// <summary>
        ///     Called when a page becomes the active page in a frame.
        /// </summary>
        /// <param name="context">
        ///     The specified <see cref="INavigationContext" />.
        /// </param>
        Task<bool> INavigableViewModel.OnNavigatingFrom(INavigationContext context)
        {
            EnsureIsNotDisposed();
            return OnNavigatingFrom(context);
        }
#else
    /// <summary>
    ///     Called when a page becomes the active page in a frame.
    /// </summary>
    /// <param name="context">
    ///     The specified <see cref="INavigationContext" />.
    /// </param>
        IOperationResult<bool> INavigableViewModel.OnNavigatingFrom(INavigationContext context)
        {
            EnsureIsNotDisposed();
            return OnNavigatingFrom(context);
        }
#endif

        #endregion

        #region Methods

        /// <summary>
        ///     Navigates back.
        /// </summary>
        protected bool GoBack()
        {
            if (!CanGoBack)
                return false;
            _navigationContext.NavigationProvider.NavigationService.GoBack();
            return true;
        }

        /// <summary>
        ///     Navigates forward.
        /// </summary>
        protected bool GoForward()
        {
            if (!CanGoForward)
                return false;
            _navigationContext.NavigationProvider.NavigationService.GoForward();
            return true;
        }

#if TASKSUPPORT
        /// <summary>
        ///     Called just before a page is no longer the active page in a frame.
        /// </summary>
        protected virtual Task<bool> OnNavigatingFrom(INavigationContext context)
        {
            return MvvmUtils.TrueTaskResult;
        }
#else
    /// <summary>
    ///     Called when a page becomes the active page in a frame.
    /// </summary>
    /// <param name="context">
    ///     The specified <see cref="INavigationContext" />.
    /// </param>
        protected virtual IOperationResult<bool> OnNavigatingFrom(INavigationContext context)
        {
            return MvvmUtils.TrueOperationResult;
        }
#endif

        /// <summary>
        ///     Called when a page is no longer the active page in a frame.
        /// </summary>
        protected virtual void OnNavigatedFrom(INavigationContext context)
        {
        }

        /// <summary>
        ///     Called when a page becomes the active page in a frame.
        /// </summary>
        protected virtual void OnNavigatedTo(INavigationContext context)
        {
        }

        /// <summary>
        ///     Occurs when an <see cref="IView" /> for the current view-model changed.
        /// </summary>
        /// <param name="oldView">
        ///     The old value of <see cref="IView" />.
        /// </param>
        /// <param name="newView">
        ///     The new value of <see cref="IView" />.
        /// </param>
        protected virtual void OnViewChanged(IView oldView, IView newView)
        {
        }

        #endregion

        #region Overrides of ViewModelBase

        /// <summary>
        ///     Occurs after the view model is fully loaded.
        /// </summary>
        internal override void OnInitializedInternal()
        {
            var relayCommand = GoBackCommand as RelayCommand;
            if (relayCommand != null)
                relayCommand.ThreadManager = ThreadManager;
        }

        #endregion
    }
}